﻿#define LOG_LEV 4
#include "oal.h"
#include "char_dev.h"
#include "psm-list.h"
#include "psm-types.h"
#define USE_LOCK 0
#if USE_LOCK
#define LIST_LOCK OAL_mutex_lock(&ctx.chrdev_list_mutex);
#define LIST_UNLOCK OAL_mutex_unlock(&ctx.chrdev_list_mutex);

#define DEV_LOCK                               \
	if (cdev->ops->fasync)                     \
	{                                          \
		OAL_mutex_lock(&cdev->work.api_mutex); \
	}
#define DEV_UNLOCK                               \
	if (cdev->ops->fasync)                       \
	{                                            \
		OAL_mutex_unlock(&cdev->work.api_mutex); \
	}
#else
#define LIST_LOCK
#define LIST_UNLOCK
#define DEV_LOCK
#define DEV_UNLOCK
#endif

//////////////////////////////////////////////////////////////////////////////////////////////////
// 框架对具体驱动的状态标志位
//////////////////////////////////////////////////////////////////////////////////////////////////
typedef enum {
	CDEV_FLAG_OPEN  = 0x1 << 0, //设备打开标志
	CDEV_FLAG_SLEEP = 0x1 << 1, //设备睡眠标志
}cdev_flags_t;

//////////////////////////////////////////////////////////////////////////////////////////////////
// 框架对具体驱动的工作空间变量，驱动有多少实例，就会有多少份。
//////////////////////////////////////////////////////////////////////////////////////////////////
typedef struct
{
	OAL_MUTEX api_mutex;
	int escape_time;
} cdev_work_space_t;

//////////////////////////////////////////////////////////////////////////////////////////////////
// 框架对具体驱动数据体，驱动有多少实例，就会有多少份。
//////////////////////////////////////////////////////////////////////////////////////////////////
typedef struct
{
	const char *name;
	file_operations_t *ops;
	cdev_work_space_t work;
	uint8_t flags;
	struct list_head node;
} cdev_t;

//////////////////////////////////////////////////////////////////////////////////////////////////
// 中断通知消息体定义
//////////////////////////////////////////////////////////////////////////////////////////////////
typedef struct
{
	file_operations_t *ops;
	unsigned long payload; 
} cdev_msg_t;

//////////////////////////////////////////////////////////////////////////////////////////////////
// 这个是 cdev 框架的全局实例变量， 无需定义类型式的广泛使用。
//////////////////////////////////////////////////////////////////////////////////////////////////
static struct cdev_private_contex
{
	struct list_head chrdev_list;
#if USE_LOCK
	OAL_MUTEX chrdev_list_mutex;
#endif
	OAL_THREAD dev_service_task;
	OAL_QUEUE dev_queue;
	int min_heart_throb;
} ctx = {0};

//////////////////////////////////////////////////////////////////////////////////////////////////
// 驱动框架服务线程任务函数。
//////////////////////////////////////////////////////////////////////////////////////////////////
static void *_dev_service_task_(void *pParameter)
{
	pParameter = pParameter;
	while (1)
	{
		int recv_ret;
		cdev_msg_t msg;
		recv_ret = OAL_queue_recv(&ctx.dev_queue, &msg, ctx.min_heart_throb);
		if (OAL_SUCCESS == recv_ret)
		{
			if (msg.ops == (file_operations_t*)(-1))
				return 0;
			else if (!msg.ops->fasync)
				continue;
			msg.ops->fasync((handle_t)msg.ops, &msg.payload);
		}
		else
		{
			LIST_LOCK
			cdev_t *cdev;
			list_for_each_entry(cdev,cdev_t, &ctx.chrdev_list, node, struct list_head)
			{
				if (!(cdev->flags & CDEV_FLAG_OPEN) ||
					(cdev->flags & CDEV_FLAG_SLEEP) ||
					!cdev->ops->polling || 
					!cdev->ops->fasync)
					continue;
				cdev->work.escape_time += ctx.min_heart_throb;
				if (cdev->work.escape_time < cdev->ops->heart_throb)
					continue;
				cdev->work.escape_time = 0;
				DEV_LOCK
				if (cdev->ops->polling((handle_t)cdev->ops))
				{
					cdev->ops->fasync((handle_t)cdev->ops, NULL);
				}
				DEV_UNLOCK
			}
			LIST_UNLOCK
		}
	}
	return 0;
}

//////////////////////////////////////////////////////////////////////////////////////////////////
// 开机首次使用，无论第一次调用哪个接口，需进行初始化驱动框架自带的变量内容，可反复调用，实际只起一次作用。
//////////////////////////////////////////////////////////////////////////////////////////////////
static void _dev_Init(void)
{
	if (ctx.dev_service_task != 0)
		return;

	memset(&ctx, 0, sizeof(ctx));
	ctx.chrdev_list.next = &ctx.chrdev_list;
	ctx.chrdev_list.prev = &ctx.chrdev_list;
	ctx.min_heart_throb = 1000;
	OAL_QUEUE_POOL pool = {sizeof(int) * 10};
	if (OAL_queue_create(&ctx.dev_queue, (char *)"_dev_service_queue_", sizeof(cdev_msg_t), &pool) != OAL_SUCCESS)
	{
		loge("OAL_queue_create _dev_service_queue_ err.\n");
		return;
	}
#if USE_LOCK	
	ctx.chrdev_list_mutex = OAL_mutex_create();
	if (!ctx.chrdev_list_mutex)
	{
		loge("OAL_mutex_create ctx.chrdev_list_mutex err.\n");
		return;
	}
#endif	
	ctx.dev_service_task = OAL_thread_create(_dev_service_task_, &ctx, "_dev_service_task_", OAL_THREAD_PRIORITY_NORMAL, 2048);
}

static void _dev_Free()
{
	if (ctx.dev_queue)
	{
		cdev_msg_t msg = { .ops = (file_operations_t*)(-1), .payload = 0,};
		OAL_queue_send(&ctx.dev_queue, &msg, OAL_WAIT_FOREVER);
	}

	if (ctx.dev_service_task)
	{
		OAL_thread_join(ctx.dev_service_task);
		ctx.dev_service_task = 0;
	}

	if (ctx.dev_queue)
	{
		OAL_queue_delete(&ctx.dev_queue);
		ctx.dev_queue = 0;
	}
}

//////////////////////////////////////////////////////////////////////////////////////////////////
// 注册和反注册只需要传入驱动设备描述表，该表可以实现扩展而无需修改注册函数参数，易于维护。
//////////////////////////////////////////////////////////////////////////////////////////////////
int register_chrdev(const dev_description_t *desp)
{
	_dev_Init();

	LIST_LOCK
	cdev_t *cdev;

	{
		list_for_each_entry(cdev, cdev_t, &ctx.chrdev_list, node, struct list_head)
		{
			if (0 == strcmp(cdev->name, desp->name))
			{
				break;
			}
		}
	}


	if (&cdev->node != &ctx.chrdev_list)
	{
		LIST_UNLOCK
		return -2;
	}

	cdev = (cdev_t *)OAL_calloc(1, sizeof(cdev_t));
	if (NULL == cdev)
	{
		loge("no enough memory to alloc cdev\n");
		LIST_UNLOCK
		return -1;
	}

	cdev->name = desp->name;
	uint32_t ver = desp->major;
	ver = ver << 16 | desp->minor;
	cdev->ops = desp->Create(ver);
	cdev->work.api_mutex = OAL_mutex_create();

	if (!cdev->work.api_mutex)
	{
		//loge("OAL_mutex_create cdev->work.api_mutex err.\n");
		LIST_UNLOCK
		return -3;
	}
	list_add_tail(&cdev->node, &ctx.chrdev_list);

	{
		list_for_each_entry(cdev, cdev_t, &ctx.chrdev_list, node, struct list_head)
		{
			if (cdev->ops->heart_throb < ctx.min_heart_throb && cdev->ops->heart_throb > 10)
			{
				ctx.min_heart_throb = cdev->ops->heart_throb;
			}
		}
	}

	LIST_UNLOCK
	return 0;
}

void unregister_chrdev(const dev_description_t *desp)
{
	cdev_t *cdev;
	LIST_LOCK
	list_for_each_entry(cdev, cdev_t, &ctx.chrdev_list, node, struct list_head)
	{
		if (0 == strcmp(cdev->name, desp->name))
		{
			DEV_LOCK
			list_del(&cdev->node);
			DEV_UNLOCK
			OAL_mutex_destroy(&cdev->work.api_mutex);
			OAL_free(cdev->ops);
			OAL_free(cdev);
			break;
		}
	}
	LIST_UNLOCK
		
	_dev_Free();
}

//////////////////////////////////////////////////////////////////////////////////////////////////
// 下面接口需被驱动开发者实现，由驱动框架来调用，而上层是通过驱动开发者提供的接口间接调用的。
//////////////////////////////////////////////////////////////////////////////////////////////////
handle_t dev_Open(const char *pathname, int flags, ...)
{
	int *p = &flags;
	void *arg = (void *)*++p;
	int ret = -1;

	cdev_t *cdev;
	LIST_LOCK
	list_for_each_entry(cdev, cdev_t, &ctx.chrdev_list, node, struct list_head)
	{
		if (0 == strcmp(cdev->name, pathname))
		{
			break;
		}
	}
	LIST_UNLOCK

	if (&cdev->node != &ctx.chrdev_list)
	{
		if (cdev->ops->open)
		{
			DEV_LOCK
			if (cdev->flags & CDEV_FLAG_OPEN) {
				ret = 0;
			}
			else {
				ret = cdev->ops->open(cdev->ops, flags, arg);
				if(0 == ret){
					cdev->flags |= CDEV_FLAG_OPEN;
				}
			}
			DEV_UNLOCK
		}
	}
	if (0 == ret)
	{
		return ((handle_t)cdev);
	}
	return (NULL);
}

int dev_Close(handle_t *handle)
{
	int ret = 0;
	cdev_t *cdev = (cdev_t *)*handle;

	if (NULL == cdev)
	{
		return (-1);
	}

	if (cdev->ops->release)
	{
		DEV_LOCK
		ret = cdev->ops->release((handle_t)cdev->ops);
		cdev->flags &= ~CDEV_FLAG_OPEN;
		DEV_UNLOCK
	}
	*handle = NULL;

	return ret;
}

ssize_t dev_Read(handle_t handle, void *buf, size_t count, loff_t *loff)
{
	cdev_t *cdev = (cdev_t *)handle;
	size_t ret = 0;
	if (NULL == cdev)
		return -1;

	if (cdev->ops->read)
	{
		DEV_LOCK
		ret = cdev->ops->read((handle_t)cdev->ops, buf, count, loff);
		DEV_UNLOCK
	}
	else
		return -2;
	return ret;
}

ssize_t dev_Write(handle_t handle, const void *buf, size_t count, loff_t *loff)
{
	cdev_t *cdev = (cdev_t *)handle;
	size_t ret = 0;
	if (NULL == cdev)
		return -1;

	if (cdev->ops->write)
	{
		DEV_LOCK
		ret = cdev->ops->write((handle_t)cdev->ops, buf, count, loff);
		DEV_UNLOCK
	}

	else
		return -2;
	return ret;
}

int dev_Ioctl(handle_t handle, unsigned long request, ...)
{
	unsigned long cmd;
	void *arg;
	unsigned long *p = &request;
	cdev_t *cdev = (cdev_t *)handle;
	int ret = 0;
	if (NULL == cdev)
		return -1;

	cmd = *p;
	arg = (void *)*++p;

	if (cdev->ops->ioctl)
	{
		DEV_LOCK
		ret = cdev->ops->ioctl((handle_t)cdev->ops, cmd, arg);
		DEV_UNLOCK
	}
	else
		return -2;
	return ret;
}

int devs_Ioctl(unsigned long request, ...)
{
	unsigned long cmd;
	void *arg;
	unsigned long *p = &request;
	int ret = 0;
	unsigned char do_ioctl;

	cmd = *p;
	arg = (void *)*++p;

	cdev_t *cdev = 0;
	list_for_each_entry(cdev, cdev_t, &ctx.chrdev_list, node, struct list_head)
	{
		if (cdev->flags & CDEV_FLAG_OPEN) 
		{
			do_ioctl = true;
				
			switch (cmd) 
			{
				case DEV_IOCTL_SLEEP:
				{
					if (cdev->flags & CDEV_FLAG_SLEEP) 
						do_ioctl = false;
					cdev->flags |= CDEV_FLAG_SLEEP;
				}
				break;
				case DEV_IOCTL_WAKEUP:
				{
					if (!(cdev->flags & CDEV_FLAG_SLEEP)) 
						do_ioctl = false;
					cdev->flags &= ~CDEV_FLAG_SLEEP;
				}
				break;
				default:
				break;
			}
			
			if (do_ioctl && cdev->ops->ioctl)
			{
				DEV_LOCK
				cdev->ops->ioctl((handle_t)cdev->ops, cmd, arg);
				DEV_UNLOCK
			}
		}
	}

	return ret;	
}
//////////////////////////////////////////////////////////////////////////////////////////////////
// 这个是驱动开发者的驱动属性如果是中断类型，在中断回调函数中通过该接口通知 _dev_service_task_ 对外进行回调。
//////////////////////////////////////////////////////////////////////////////////////////////////
void dev_IRQNotify(handle_t handle, unsigned long payload)
{	
	if (NULL == handle)
		return;

	file_operations_t *fops = (file_operations_t *)handle;
	if (!fops->fasync)
		return;

	cdev_msg_t msg;
	msg.ops = fops;
	msg.payload = payload;
	OAL_queue_send(&ctx.dev_queue, &msg, OAL_WAIT_FOREVER);
}
